#include "lenstraybuffer.h"

LensTrayBuffer::LensTrayBuffer(LensTrayModuleConfig *ltConfig,
                               LensTrayModuleState *ltState,
                               LensTrayMagazine *ltMag,
                               QObject *parent)
    : Worker(WorkerName::LensTrayBuffer, parent), ltConfig(ltConfig), ltState(ltState), ltMag(ltMag)
{
}

void LensTrayBuffer::initEventHandle()
{
    ltBufferLeftTraySensor = MotionElementContainer::getIns()->getItem<SCDI>(DiName::LTBufferLeftTraySNR);
    ltBufferRightTraySensor = MotionElementContainer::getIns()->getItem<SCDI>(DiName::LTBufferRightTraySNR);
    ltBufferBeltZ = MotionElementContainer::getIns()->getItem<SingleAxis>(SAxisModuleName::LTBufferBeltZ);
    ltBufferBelt = MotionElementContainer::getIns()->getItem<SCAxis>(AxisName::LTBufferBelt);

    beltRunDir = SCAxis::DirEnumInfo().nameToEnum(ltConfig->ltBufferBeltRunDir());
}

void LensTrayBuffer::startEventHandle(const WorkerEvent &event)
{
    Q_UNUSED(event)
    hasSynchronizeHasTray = false;
    isLtMagStopped = false;
}

void LensTrayBuffer::allWorkerStopped()
{
    isLtMagStopped = false;
}

void LensTrayBuffer::purgeEventHandle()
{
    purgeEventHandled();
}

void LensTrayBuffer::resetEventHandle()
{
    isLtMagStopped = false;
}

void LensTrayBuffer::homeWorker(bool moveToReadyPosAfterHome)
{
    Q_UNUSED(moveToReadyPosAfterHome)
}

void LensTrayBuffer::getNewTray()
{
    checkIsInit();

    if (!hasSynchronizeHasTray)
    {
        synchronizeHasTray();
        hasSynchronizeHasTray = true;
    }
    if (!ltState->lensTrayBufferHasTray())
    {
        getNewTrayImpl();
        if (isLtMagStopped)
        {
            handleStop();
            return;
        }
        if (ltState->lensTrayLoaderHasTray())
        {
            ltMag->pushEvent(WorkerEvent("moveToFirstInUseTrayPos"));
        }
        else
        {
            ltMag->pushEvent(WorkerEvent("moveToNextRawTrayPos"));
        }
    }
}

void LensTrayBuffer::ltMagArrivedNextRawTrayPos()
{
    isLTMagArrivedNextRawTrayPos = true;
}

void LensTrayBuffer::ltMagStopped()
{
    isLtMagStopped = true;
    if (!isRunning())
    {
        handleStop();
    }
}

void LensTrayBuffer::manuallyGetNewTray(bool showTip)
{
    checkIsInit();

    synchronizeHasTray();
    if (ltState->lensTrayBufferHasTray())
    {
        if (showTip)
        {
            UIOperation::getIns()->showTip(tr("Lens tray buffer already detected tray!"));
        }
        return;
    }
    getNewTrayImpl();
}

void LensTrayBuffer::synchronizeHasTray()
{
    ltState->setLensTrayBufferHasTray(detectReallyHasTray());
}

bool LensTrayBuffer::detectReallyHasTray()
{
    if (ltBufferRightTraySensor->get(-1))
    {
        return true;
    }
    ltBufferBelt->velocityMove(beltRunDir);
    QElapsedTimer timer;
    timer.start();
    while (timer.elapsed() < ltConfig->detectHasTrayMaxTime())
    {
        if (ltBufferRightTraySensor->get(-1))
        {
            break;
        }
        QThread::msleep(10);
    }
    ltBufferBelt->stop();
    return ltBufferRightTraySensor->get(-1);
}

void LensTrayBuffer::getNewTrayImpl()
{
    if (isLtMagStopped)
    {
        handleStop();
        return;
    }

    m_isGettingNewTray = true;
    AutoResetBool(&m_isGettingNewTray, false);

    isLTMagArrivedNextRawTrayPos = false;
    QVariantList args;
    args << true;
    ltMag->pushEvent(WorkerEvent("moveToNextRawTrayPos", args));
    ltBufferBeltZ->moveTo(SAxisPosName::LoadTrayPos);

    QElapsedTimer timer;
    timer.start();
    while (true)
    {
        if (hasAbortEvent())
        {
            return;
        }
        if (isLtMagStopped)
        {
            handleStop();
            return;
        }

        if (isLTMagArrivedNextRawTrayPos)
        {
            break;
        }
        if (timer.elapsed() > 20000)
        {
            timer.restart();
            qCWarning(logCate()) << tr("Waiting lens tray magazine move to next raw tray pos...");
        }
        QThread::msleep(10);
    }

    while (true)
    {
        ltMag->kickTray();
        try
        {
            ErrorHandler::tryToHandleError([this] {
                if (!ltBufferLeftTraySensor->get(-1))
                {
                    ltMag->retractKicker();
                    throw LTBufferDetectTrayError();
                }
            });
            break;
        }
        catch (UnhandledError &ue)
        {
            ue.what();
            if (ue.errHandlingOption == ReKick)
            {
                continue;
            }
            else    // Skip
            {
                ltMag->setCurrentTrayState(MagTrayState::NoTray);
                getNewTrayImpl();
                return;
            }
        }
    }

    ltMag->setCurrentTrayState(MagTrayState::InUse);
    ltMag->pushEvent(WorkerEvent("retractKicker", QVariant(), 3000));

    ltBufferBelt->velocityMove(beltRunDir);
    ErrorHandler::tryToHandleGeneralError<void>(
        [this] {
            QElapsedTimer timer;
            timer.start();
            while (true)
            {
                if (ltBufferRightTraySensor->get(-1))
                {
                    ltBufferBelt->stop();
                    break;
                }
                if (timer.elapsed() > ltConfig->ltBufferGetTrayTimeout())
                {
                    ltBufferBelt->stop();
                    throw GeneralError(workerName(), tr("Get tray timeout!"));
                }
                QThread::msleep(10);
            }
        },
        [this] { ltBufferBelt->velocityMove(beltRunDir); });

    ltState->setLensTrayBufferHasTray(true);
    ltMag->setIsLoadingUnloadingTray(false);
}

void LensTrayBuffer::handleStop()
{
    clearEvent();
    stopEventHandled();
    emit sendEvent(WorkerName::LensTrayLoader, WorkerEvent("ltBufferStopped"));
}
